package com.yu.config.redis;

import com.github.pagehelper.util.StringUtil;
import com.yu.config.spring.SpringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

@Slf4j
public class RedisLock implements Lock {

	private final RedisTemplate redisTemplate = SpringUtils.getBean(RedisTemplate.class);
	/**
	 * redis中锁住存储的值
	 */
    private static final String LOCKED = "LOCKED";
    /**
     * redis缓存模块
     */
    private static final String MODULE = "LOCK:";


    /**
     * 30000毫秒内去设置redis,无法设置则
     */
    private static final long TIME_OUT = 30000;

    /**
     * 失效时间 默认60s
     */
    public static final int EXPIRE = 60;

    /**
     * 存储在redis中的key
     */
    private String key;

    /**
     * 状态标志，=true 对应的key加锁成功
     * unlock后= false
     */
    private volatile boolean locked = false;
    /**
     * 存储锁对象（防止重复创建锁），根据具体业务
     */
    private static ConcurrentMap<String, RedisLock> map =new ConcurrentHashMap<>();

    /**
     * 设置锁的key
     * @param key
     */
    public RedisLock(String key){
    	 this.key =  key;
    }
    /**
     * 验证当前设置的key是否被锁住，
     * @return 返回true表示资源被锁，返回false表示资源空闲中
     * @createDate 2016年12月15日
     * @updateDate
     */
    public boolean isLock(){
    	String str = (String) redisTemplate.opsForValue().get(this.key);
    	if(StringUtil.isNotEmpty(str)){
    		return Boolean.TRUE;
    	}
    	return Boolean.FALSE;
    }
    /**
     * 查看锁是否被锁住，加锁住可以调用这个判断
     * @return
     * @createDate 2017年8月25日
     * @updateDate
     */
    public boolean isLocked(){
    	return this.locked;
    }

    /**
     * 加锁
     * @param timeout  获取锁的时间，即业务等待时间
     * @createDate 2016年9月23日
     * @updateDate
     */
    public boolean lock(long timeout) {
    	return lock(timeout,EXPIRE);
    }
    /**
     * 加锁,默认30秒去获取锁资源
     * @param lockTime   锁多长时间释放
     * @createDate 2017年8月25日
     * @updateDate
     */
    public boolean lockTime(int lockTime) {
    	return lock(TIME_OUT,lockTime);
    }
    /**
     * 加锁
     * @param timeout  获取锁的时间，即业务等待时间
     * @param lockTime  锁多长时间释放(不unlock的情况下)
     * @createDate 2016年9月23日
     * @updateDate
     */
    public boolean lock(long timeout,int  lockTime) {
    	long nano = System.nanoTime();
    	timeout *= 1000000;
    	final Random r = new Random();
    	try {
    		while ((System.nanoTime() - nano) < timeout) {
    			if (setnx(key, LOCKED)) {
    				if(lockTime > 0){
    					redisTemplate.expire(key, lockTime, TimeUnit.SECONDS);
    				}else{
    					redisTemplate.expire(key, EXPIRE, TimeUnit.SECONDS);
    				}
    				locked = true;
    				log.debug("add RedisLock[" + key + "].");
    				break;
    			}
    			Thread.sleep(100, r.nextInt(500));
    		}
    	} catch (Exception e) {
    		log.error("",e);
    	}
    	return locked;
    }

    public boolean setnx(String key, String locked) {
        return (boolean) redisTemplate.execute((RedisCallback) connection -> {
            //获取锁
            Boolean acquire = connection.setNX(key.getBytes(), String.valueOf(locked).getBytes());
            if (acquire) {
                return true;
            }
            return false;
        });
    }

    /**
     * 加锁
     * 默认业务等待30秒，如果30秒还未获取到锁，则业务还会往下走，
     * 最好使用isLocked()判断是否加锁成功
     */
	@Override
	public void lock() {
		lock(TIME_OUT);
	}

	@Override
	public void lockInterruptibly() throws InterruptedException {
	}

	@Override
	public boolean tryLock() {
		return false;
	}

	@Override
	public boolean tryLock(long time, TimeUnit unit)
			throws InterruptedException {
		return false;
	}

	@Override
	public void unlock() {
		unlock(true);
	}
	/**
	 * 释放资源
	 * @param isClear 是否从map中清除 true清除（防止重复创建锁）
	 * @createDate 2016年9月23日
	 * @updateDate
	 */
	public void unlock(boolean isClear) {
		if (locked) {
            log.debug("release RedisLock[" + key + "].");
            if(isClear){
                //map中有就删除
                map.remove(key);
            }
            redisTemplate.delete(key);
            locked = false;
        }
	}

	@Override
	public Condition newCondition() {
		return null;
	}

}
